/*
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef FLATBUFFERS_STL_EMULATION_H_
#define FLATBUFFERS_STL_EMULATION_H_

// clang-format off

#include <string>
#include <type_traits>
#include <vector>
#include <memory>
#include <limits>

#if defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL)
#define FLATBUFFERS_CPP98_STL
#endif  // defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL)

#if defined(FLATBUFFERS_CPP98_STL)
#include <cctype>
#endif  // defined(FLATBUFFERS_CPP98_STL)

// This header provides backwards compatibility for C++98 STLs like stlport.
namespace flatbuffers
{

// Retrieve ::back() from a string in a way that is compatible with pre C++11
// STLs (e.g stlport).
inline char string_back(const std::string &value)
{
    return value[value.length() - 1];
}

// Helper method that retrieves ::data() from a vector in a way that is
// compatible with pre C++11 STLs (e.g stlport).
template <typename T> inline T *vector_data(std::vector<T> &vector)
{
    // In some debug environments, operator[] does bounds checking, so &vector[0]
    // can't be used.
    return &(*vector.begin());
}

template <typename T> inline const T *vector_data(
    const std::vector<T> &vector)
{
    return &(*vector.begin());
}

template <typename T, typename V>
inline void vector_emplace_back(std::vector<T> *vector, V &&data)
{
#if defined(FLATBUFFERS_CPP98_STL)
    vector->push_back(data);
#else
    vector->emplace_back(std::forward<V>(data));
#endif  // defined(FLATBUFFERS_CPP98_STL)
}

#ifndef FLATBUFFERS_CPP98_STL
#if !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */)
template <typename T>
using numeric_limits = std::numeric_limits<T>;
#else
template <typename T> class numeric_limits :
    public std::numeric_limits<T> {};
#endif  // !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */)
#else
template <typename T> class numeric_limits :
    public std::numeric_limits<T> {};

template <> class numeric_limits<unsigned long long>
{
public:
    static unsigned long long min()
    {
        return 0ULL;
    }
    static unsigned long long max()
    {
        return ~0ULL;
    }
};

template <> class numeric_limits<long long>
{
public:
    static long long min()
    {
        return static_cast<long long>(1ULL << ((sizeof(long long) << 3) - 1));
    }
    static long long max()
    {
        return static_cast<long long>(
                   (1ULL << ((sizeof(long long) << 3) - 1)) - 1);
    }
};
#endif  // FLATBUFFERS_CPP98_STL

#if !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */)
#ifndef FLATBUFFERS_CPP98_STL
template <typename T> using is_scalar = std::is_scalar<T>;
template <typename T, typename U> using is_same = std::is_same<T,U>;
template <typename T> using is_floating_point = std::is_floating_point<T>;
template <typename T> using is_unsigned = std::is_unsigned<T>;
#else
// Map C++ TR1 templates defined by stlport.
template <typename T> using is_scalar = std::tr1::is_scalar<T>;
template <typename T, typename U> using is_same = std::tr1::is_same<T,U>;
template <typename T> using is_floating_point =
    std::tr1::is_floating_point<T>;
template <typename T> using is_unsigned = std::tr1::is_unsigned<T>;
#endif  // !FLATBUFFERS_CPP98_STL
#else
// MSVC 2010 doesn't support C++11 aliases.
template <typename T> struct is_scalar : public std::is_scalar<T> {};
template <typename T, typename U> struct is_same : public std::is_same<T,U> {};
template <typename T> struct is_floating_point :
    public std::is_floating_point<T> {};
template <typename T> struct is_unsigned : public std::is_unsigned<T> {};
#endif  // !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */)

#ifndef FLATBUFFERS_CPP98_STL
#if !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */)
template <class T> using unique_ptr = std::unique_ptr<T>;
#else
// MSVC 2010 doesn't support C++11 aliases.
// We're manually "aliasing" the class here as we want to bring unique_ptr
// into the flatbuffers namespace.  We have unique_ptr in the flatbuffers
// namespace we have a completely independent implemenation (see below)
// for C++98 STL implementations.
template <class T> class unique_ptr : public std::unique_ptr<T>
{
public:
    unique_ptr() {}
    explicit unique_ptr(T* p) : std::unique_ptr<T>(p) {}
    unique_ptr(std::unique_ptr<T>&& u)
    {
        *this = std::move(u);
    }
    unique_ptr(unique_ptr&& u)
    {
        *this = std::move(u);
    }
    unique_ptr& operator=(std::unique_ptr<T>&& u)
    {
        std::unique_ptr<T>::reset(u.release());
        return *this;
    }
    unique_ptr& operator=(unique_ptr&& u)
    {
        std::unique_ptr<T>::reset(u.release());
        return *this;
    }
    unique_ptr& operator=(T* p)
    {
        return std::unique_ptr<T>::operator=(p);
    }
};
#endif  // !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */)
#else
// Very limited implementation of unique_ptr.
// This is provided simply to allow the C++ code generated from the default
// settings to function in C++98 environments with no modifications.
template <class T> class unique_ptr
{
public:
    typedef T element_type;

    unique_ptr() : ptr_(nullptr) {}
    explicit unique_ptr(T* p) : ptr_(p) {}
    unique_ptr(unique_ptr&& u) : ptr_(nullptr)
    {
        reset(u.release());
    }
    unique_ptr(const unique_ptr& u) : ptr_(nullptr)
    {
        reset(const_cast<unique_ptr*>(&u)->release());
    }
    ~unique_ptr()
    {
        reset();
    }

    unique_ptr& operator=(const unique_ptr& u)
    {
        reset(const_cast<unique_ptr*>(&u)->release());
        return *this;
    }

    unique_ptr& operator=(unique_ptr&& u)
    {
        reset(u.release());
        return *this;
    }

    unique_ptr& operator=(T* p)
    {
        reset(p);
        return *this;
    }

    const T& operator*() const
    {
        return *ptr_;
    }
    T* operator->() const
    {
        return ptr_;
    }
    T* get() const noexcept
    {
        return ptr_;
    }
    explicit operator bool() const
    {
        return ptr_ != nullptr;
    }

    // modifiers
    T* release()
    {
        T* value = ptr_;
        ptr_ = nullptr;
        return value;
    }

    void reset(T* p = nullptr)
    {
        T* value = ptr_;
        ptr_ = p;
        if (value) delete value;
    }

    void swap(unique_ptr& u)
    {
        T* temp_ptr = ptr_;
        ptr_ = u.ptr_;
        u.ptr_ = temp_ptr;
    }

private:
    T* ptr_;
};

template <class T> bool operator==(const unique_ptr<T>& x,
                                   const unique_ptr<T>& y)
{
    return x.get() == y.get();
}

template <class T, class D> bool operator==(const unique_ptr<T>& x,
        const D* y)
{
    return static_cast<D*>(x.get()) == y;
}

template <class T> bool operator==(const unique_ptr<T>& x, intptr_t y)
{
    return reinterpret_cast<intptr_t>(x.get()) == y;
}
#endif  // !FLATBUFFERS_CPP98_STL

}  // namespace flatbuffers

#endif  // FLATBUFFERS_STL_EMULATION_H_
